﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Urs.Admin.Models.Common;
using Urs.Admin.Models.Orders;
using Urs.Core;
using Urs.Data.Domain.Stores;
using Urs.Data.Domain.Common;
using Urs.Data.Domain.Directory;
using Urs.Data.Domain.Orders;
using Urs.Data.Domain.Payments;
using Urs.Data.Domain.Shipping;
using Urs.Data.Domain.Configuration;
using Urs.Services;
using Urs.Services.Stores;
using Urs.Services.Common;
using Urs.Services.Coupons;
using Urs.Services.Directory;
using Urs.Services.ExportImport;
using Urs.Services.Localization;
using Urs.Services.Media;
using Urs.Services.Orders;
using Urs.Services.Payments;
using Urs.Services.Security;
using Urs.Services.Shipping;
using Urs.Framework;
using Urs.Framework.Controllers;
using Urs.Framework.Extensions;
using Urs.Framework.Kendoui;

namespace Urs.Admin.Controllers
{
    [AdminAuthorize]
    public partial class OrderController : BaseAdminController
    {
        #region Fields

        private readonly IOrderService _orderService;
        private readonly IPictureService _pictureService;
        private readonly IOrderReportService _orderReportService;
        private readonly IOrderProcessingService _orderProcessingService;
        private readonly ILocalizationService _localizationService;
        private readonly IWorkContext _workContext;
        private readonly IEncryptionService _encryptionService;
        private readonly IPaymentService _paymentService;
        private readonly IMeasureService _measureService;
        private readonly IPdfService _pdfService;
        private readonly IAddressService _addressService;
        private readonly IGoodsService _goodsService;
        private readonly IExportManager _exportManager;
        private readonly IPermissionService _permissionService;
        private readonly ICategoryService _categoryService;
        private readonly IBrandService _brandService;
        private readonly IGoodsSpecService _goodsSpecService;
        private readonly IGoodsSpecParser _goodsSpecParser;
        private readonly IGoodsSpecFormatter _goodsSpecFormatter;
        private readonly IShoppingCartService _shoppingCartService;
        private readonly IShipmentService _shipmentService;
        private readonly StoreSettings _storeSettings;
        private readonly MeasureSettings _measureSettings;
        private readonly PdfSettings _pdfSettings;
        private readonly ShippingSettings _shippingSettings;
        private readonly AddressSettings _addressSettings;
        private readonly BillingAddressSettings _billingAddressSettings;
        private readonly IAreaService _areaService;
        private readonly ICouponService _couponService;
        #endregion

        #region Ctor

        public OrderController(IOrderService orderService,
            IPictureService pictureService,
            IOrderReportService orderReportService,
            IOrderProcessingService orderProcessingService,
            ILocalizationService localizationService,
            IWorkContext workContext,
            IEncryptionService encryptionService,
            IPaymentService paymentService,
            IMeasureService measureService,
            IPdfService pdfService,
            IAddressService addressService,
            IGoodsService goodsService,
            IExportManager exportManager,
            IPermissionService permissionService,
            ICategoryService categoryService,
            IBrandService brandService,
            IGoodsSpecService goodsSpecService,
            IGoodsSpecParser goodsSpecParser,
            IGoodsSpecFormatter goodsSpecFormatter,
            IShoppingCartService shoppingCartService,
            IShipmentService shipmentService,
            StoreSettings storeSettings,
            MeasureSettings measureSettings,
            PdfSettings pdfSettings,
            ShippingSettings shippingSettings,
            AddressSettings addressSettings,
            BillingAddressSettings billingAddressSettings,
            IAreaService areaService,
            ICouponService couponService)
        {
            this._orderService = orderService;
            this._pictureService = pictureService;
            this._orderReportService = orderReportService;
            this._orderProcessingService = orderProcessingService;
            this._localizationService = localizationService;
            this._workContext = workContext;
            this._encryptionService = encryptionService;
            this._paymentService = paymentService;
            this._measureService = measureService;
            this._pdfService = pdfService;
            this._addressService = addressService;
            this._goodsService = goodsService;
            this._exportManager = exportManager;
            this._permissionService = permissionService;
            this._categoryService = categoryService;
            this._brandService = brandService;
            this._goodsSpecService = goodsSpecService;
            this._goodsSpecParser = goodsSpecParser;
            this._goodsSpecFormatter = goodsSpecFormatter;
            this._shoppingCartService = shoppingCartService;
            this._shipmentService = shipmentService;
            this._storeSettings = storeSettings;
            this._measureSettings = measureSettings;
            this._pdfSettings = pdfSettings;
            this._shippingSettings = shippingSettings;
            this._addressSettings = addressSettings;
            this._billingAddressSettings = billingAddressSettings;
            this._areaService = areaService;
            this._couponService = couponService;
        }

        #endregion

        #region Utilities

        [NonAction]
        protected void PrepareOrderDetailsModel(OrderModel model, Order order)
        {
            if (order == null)
                throw new ArgumentNullException("order");

            if (model == null)
                throw new ArgumentNullException("model");

            model.Id = order.Id;
            model.OrderStatusId = order.OrderStatusId;
            model.OrderStatus = order.OrderStatus.GetLocalizedEnum(_localizationService);
            model.OrderGuid = order.OrderGuid;
            model.UserId = order.UserId;
            model.UserFullName = order.User.Nickname;
            model.UserIp = order.OrderIp;
            model.CreateTime = order.CreateTime;
            model.DisplayPdfInvoice = _pdfSettings.Enabled;
            model.AffiliateId = order.AffiliateId;
            model.Remark = order.Remark;
            model.CouponId = order.CouponId;
            if (model.CouponId.HasValue)
            {
                var coupon = _couponService.GetCouponById(model.CouponId.Value);
                if (coupon != null)
                    model.CouponMsg = string.Format("{0} 优化金额：{1}", coupon.Title, coupon.Value);
            }

            #region Order totals

            //subtotal
            model.OrderSubtotal = PriceFormatter.FormatPrice(order.OrderSubtotal);
            model.OrderSubtotalValue = order.OrderSubtotal;

            //shipping
            model.OrderShipping = PriceFormatter.FormatPrice(order.OrderShipping);
            model.OrderShippingValue = order.OrderShipping;

            //payment method additional fee
            model.PaymentMethodAdditionalFee = PriceFormatter.FormatPrice(order.PaymentMethodAdditionalFee);
            model.PaymentMethodAdditionalFeeValue = order.PaymentMethodAdditionalFee;

            //reward points
            if (order.PointsEntry != null)
            {
                model.RedeemedRewardPoints = -order.PointsEntry.Points;
                model.RedeemedRewardPointsAmount = PriceFormatter.FormatPrice(-order.PointsEntry.UsedAmount);
            }

            //total
            model.OrderTotal = PriceFormatter.FormatPrice(order.OrderTotal);
            model.OrderTotalValue = order.OrderTotal;

            model.AccountAmount = PriceFormatter.FormatPrice(order.AccountAmount);
            //refunded amount
            if (order.RefundedAmount > decimal.Zero)
                model.RefundedAmount = PriceFormatter.FormatPrice(order.RefundedAmount);


            #endregion

            #region Payment info

            //purchase order number (we have to find a better to inject this information because it's related to a certain plugin)
            var pm = _paymentService.LoadPaymentMethodBySystemName(order.PaymentMethodSystemName);

            //payment method info
            model.PaymentMethod = pm != null ? pm.PluginDescriptor.FriendlyName : order.PaymentMethodSystemName ?? "-----";
            model.PaymentStatusId = order.PaymentStatusId;
            model.PaymentStatus = order.PaymentStatus.GetLocalizedEnum(_localizationService);

            //payment method buttons
            model.CanCancelOrder = _orderProcessingService.CanCancelOrder(order);
            model.CanCapture = _orderProcessingService.CanCapture(order);
            model.CanMarkOrderAsPaid = _orderProcessingService.CanMarkOrderAsPaid(order);
            model.CanRefund = _orderProcessingService.CanRefund(order);
            model.CanRefundOffline = _orderProcessingService.CanRefundOffline(order);
            model.CanPartiallyRefund = _orderProcessingService.CanPartiallyRefund(order, decimal.Zero);
            model.CanPartiallyRefundOffline = _orderProcessingService.CanPartiallyRefundOffline(order, decimal.Zero);
            model.CanVoid = _orderProcessingService.CanVoid(order);
            model.CanVoidOffline = _orderProcessingService.CanVoidOffline(order);
            model.PrimaryStoreCurrencyCode = string.Empty;
            model.MaxAmountToRefund = order.OrderTotal - order.RefundedAmount;

            #endregion

            #region Shipping info

            model.ShippingStatus = order.ShippingStatus.GetLocalizedEnum(_localizationService); ;
            if (order.ShippingStatus != ShippingStatus.ShippingNotRequired)
            {
                model.IsShippable = true;
                model.ShippingAddress = order.ShippingAddress.ToModel<AddressModel>();
                model.ShippingMethod = order.ShippingMethod;

                model.ShippingAddress.NameEnabled = true;
                model.ShippingAddress.NameRequired = true;
                model.ShippingAddress.CompanyEnabled = _addressSettings.CompanyEnabled;
                model.ShippingAddress.CompanyRequired = _addressSettings.CompanyRequired;
                model.ShippingAddress.ProvincesEnabled = _addressSettings.ProvincesEnabled;
                model.ShippingAddress.LatitudeEnabled = _addressSettings.LatitudeEnabled;
                model.ShippingAddress.LongitudeEnabled = _addressSettings.LongitudeEnabled;
                model.ShippingAddress.StreetAddressEnabled = _addressSettings.StreetAddressEnabled;
                model.ShippingAddress.StreetAddressRequired = _addressSettings.StreetAddressRequired;
                model.ShippingAddress.StreetAddress2Enabled = _addressSettings.StreetAddress2Enabled;
                model.ShippingAddress.StreetAddress2Required = _addressSettings.StreetAddress2Required;
                model.ShippingAddress.ZipPostalCodeEnabled = _addressSettings.ZipPostalCodeEnabled;
                model.ShippingAddress.ZipPostalCodeRequired = _addressSettings.ZipPostalCodeRequired;
                model.ShippingAddress.PhoneEnabled = _addressSettings.PhoneEnabled;
                model.ShippingAddress.PhoneRequired = _addressSettings.PhoneRequired;
                model.ShippingAddress.FaxEnabled = _addressSettings.FaxEnabled;
                model.ShippingAddress.FaxRequired = _addressSettings.FaxRequired;
            }
            #endregion

            #region Goodss
            model.CheckoutAttributeInfo = order.CheckoutAttributeDescription;
            foreach (var op in order.orderItems)
            {
                var opModel = new OrderModel.OrderGoodsModel()
                {
                    Id = op.Id,
                    GoodsId = op.GoodsId,
                    Sku = op.Sku,
                    Quantity = op.Quantity
                };

                //goods name
                opModel.GoodsName = op.GoodsName;
                var defaultGoodsPicture = _pictureService.GetPicturesByGoodsId(op.GoodsId, 1).FirstOrDefault();
                opModel.PictureThumbnailUrl = _pictureService.GetPictureUrl(defaultGoodsPicture, 75, true);
                //unit price
                opModel.UnitPriceValue = op.UnitPrice;
                opModel.UnitPrice = PriceFormatter.FormatPrice(op.UnitPrice);
                //subtotal
                opModel.SubTotalValue = op.Price;
                opModel.SubTotal = PriceFormatter.FormatPrice(op.Price);
                opModel.AttributeInfo = op.AttributeDescription;
                //return requests
                //opModel.AfterSalesIds = _orderService.SearchAfterSales(0, op.Id, null, 0, int.MaxValue)
                //.Select(rr => rr.Id).ToList();

                model.Items.Add(opModel);
            }
            #endregion
        }

        [NonAction]
        protected OrderModel.AddOrderGoodsModel.GoodsDetailsModel PrepareAddGoodsToOrderModel(int orderId, int goodsId)
        {

            var goods = _goodsService.GetGoodsById(goodsId);
            var model = new OrderModel.AddOrderGoodsModel.GoodsDetailsModel()
            {
                GoodsId = goodsId,
                OrderId = orderId,
                Name = goods.Name,
                UnitPrice = decimal.Zero,
                Quantity = 1,
                SubTotal = decimal.Zero,
            };

            //attributes
            var goodsSpecMappings = _goodsSpecService.GetGoodsSpecMappingByGoodsId(goods.Id);
            foreach (var mapping in goodsSpecMappings)
            {
                var pvaModel = new OrderModel.AddOrderGoodsModel.GoodsVariantAttributeModel()
                {
                    Id = mapping.Id,
                    GoodsSpecId = mapping.GoodsSpecId,
                    Name = mapping.GoodsSpecName
                };

                //values
                var pvaValues = _goodsSpecService.GetGoodsSpecValues(mapping.GoodsSpecId);
                foreach (var pvaValue in pvaValues)
                {
                    var pvaValueModel = new OrderModel.AddOrderGoodsModel.GoodsSpecValueModel()
                    {
                        Id = pvaValue.Id,
                        Name = pvaValue.Name
                    };
                    pvaModel.Values.Add(pvaValueModel);
                }

                model.GoodsSpecs.Add(pvaModel);
            }

            return model;
        }

        [NonAction]
        protected ShipmentModel PrepareShipmentModel(Shipment shipment, bool prepareGoodss)
        {
            //measures
            var baseWeight = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId);
            var baseWeightIn = baseWeight != null ? baseWeight.Name : "";
            var baseDimension = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId);
            var baseDimensionIn = baseDimension != null ? baseDimension.Name : "";

            var model = new ShipmentModel()
            {
                Id = shipment.Id,
                OrderId = shipment.OrderId,
                ShippingMethod = shipment.ShippingMethod,
                ExpressName = shipment.ExpressName,
                TrackingNumber = shipment.TrackingNumber,
                TotalWeight = shipment.TotalWeight.HasValue ? string.Format("{0:F2} [{1}]", shipment.TotalWeight, baseWeightIn) : "",
                ShippedDate = shipment.ShippedTime.HasValue ? shipment.ShippedTime.Value.ToString() : _localizationService.GetResource("Admin.Orders.Shipments.ShippedDate.NotYet"),
                CanShip = !shipment.ShippedTime.HasValue,
                DeliveryDate = shipment.DeliveryTime.HasValue ? shipment.DeliveryTime.Value.ToString() : _localizationService.GetResource("Admin.Orders.Shipments.DeliveryDate.NotYet"),
                CanDeliver = shipment.ShippedTime.HasValue && !shipment.DeliveryTime.HasValue,
                DisplayPdfPackagingSlip = _pdfSettings.Enabled,
            };

            if (_shippingSettings.ActiveShippingExpressNames != null)
                foreach (var rrr in _shippingSettings.ActiveShippingExpressNames)
                {
                    model.AvailableExpressNames.Add(new SelectListItem()
                    {
                        Text = rrr,
                        Value = rrr
                    });
                }

            if (prepareGoodss)
            {
                foreach (var sopv in shipment.ShipmentorderItems)
                {
                    var opv = _orderService.GetOrderGoodsById(sopv.OrderItemId);
                    if (opv == null)
                        continue;

                    //quantities
                    var qtyInThisShipment = sopv.Quantity;
                    var maxQtyToAdd = opv.GetTotalNumberOfItemsCanBeAddedToShipment();
                    var qtyOrdered = opv.Quantity;
                    var qtyInAllShipments = opv.GetTotalNumberOfItemsInAllShipment();

                    var sopvModel = new ShipmentModel.ShipmentOrderGoodsModel()
                    {
                        Id = sopv.Id,
                        OrderGoodsId = opv.Id,
                        GoodsId = opv.GoodsId,
                        Sku = opv.Goods.FormatSku(opv.AttributesXml, _goodsSpecParser),
                        AttributeInfo = opv.AttributeDescription,
                        ItemWeight = opv.ItemWeight.HasValue ? string.Format("{0:F2} [{1}]", opv.ItemWeight, baseWeightIn) : "",
                        ItemDimensions = string.Format("{0:F2} x {1:F2} x {2:F2} [{3}]", opv.Goods.Length, opv.Goods.Width, opv.Goods.Height, baseDimensionIn),
                        QuantityOrdered = qtyOrdered,
                        QuantityInThisShipment = qtyInThisShipment,
                        QuantityInAllShipments = qtyInAllShipments,
                        QuantityToAdd = maxQtyToAdd,
                    };

                    //goods name
                    sopvModel.FullGoodsName = opv.GoodsName;
                    model.Goodss.Add(sopvModel);
                }
            }
            return model;
        }

        [NonAction]
        protected IList<ShipmentGoodsModel> PrepareShipmentGoodsModel(Order order)
        {
            //measures
            var baseWeight = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId);
            var baseWeightIn = baseWeight != null ? baseWeight.Name : "";
            var baseDimension = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId);
            var baseDimensionIn = baseDimension != null ? baseDimension.Name : "";

            var list = new List<ShipmentGoodsModel>();
            foreach (var item in order.orderItems)
            {
                var model = new ShipmentGoodsModel()
                {
                    Id = item.Id,
                    OrderId = order.Id,
                    ShippingStatus = order.ShippingStatus.GetLocalizedEnum(_localizationService),
                    GoodsId = item.GoodsId,
                    GoodsName = item.GoodsName,
                    Sku = item.Goods.FormatSku(item.AttributesXml, _goodsSpecParser),
                    AttributeInfo = item.AttributeDescription,
                    ItemWeight = item.ItemWeight.HasValue ? string.Format("{0:F2} [{1}]", item.ItemWeight, baseWeightIn) : "",
                    ItemDimensions = string.Format("{0:F2} x {1:F2} x {2:F2} [{3}]", item.Goods.Length, item.Goods.Width, item.Goods.Height, baseDimensionIn),
                    Quantity = item.Quantity
                };
                var shipment = _shipmentService.GetShipmentByOrderId(order.Id);

                if (shipment != null)
                {
                    model.ShippingMethod = shipment.ShippingMethod;
                    model.ExpressName = shipment.ExpressName;
                    model.TrackingNumber = shipment.TrackingNumber;
                    model.TotalWeight = shipment.TotalWeight.HasValue ? string.Format("{0:F2} [{1}]", shipment.TotalWeight, baseWeightIn) : "";
                    model.ShippedDate = shipment.ShippedTime.HasValue ? shipment.ShippedTime.Value.ToString() : _localizationService.GetResource("Admin.Orders.Shipments.ShippedDate.NotYet");
                    model.CanShip = !shipment.ShippedTime.HasValue;
                    model.DeliveryDate = shipment.DeliveryTime.HasValue ? shipment.DeliveryTime.Value.ToString() : _localizationService.GetResource("Admin.Orders.Shipments.DeliveryDate.NotYet");
                    model.CanDeliver = shipment.ShippedTime.HasValue && !shipment.DeliveryTime.HasValue;
                }
                list.Add(model);
            }
            return list;
        }

        #endregion

        #region Order list

        public IActionResult Index()
        {
            return RedirectToAction("List");
        }

        public IActionResult List()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var model = new OrderListModel();
            model.AvailableOrderStatuses = OrderStatus.Pending.ToSelectList(false).ToList();
            model.AvailableOrderStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0", Selected = true });

            model.AvailablePaymentStatuses = PaymentStatus.Pending.ToSelectList(false).ToList();
            model.AvailablePaymentStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0", Selected = true });

            model.AvailableShippingStatuses = ShippingStatus.NotYetShipped.ToSelectList(false).ToList();
            model.AvailableShippingStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0", Selected = true });

            //payment methods
            model.AvailablePaymentMethods.Add(new SelectListItem { Text = _localizationService.GetResource("Admin.Common.All"), Value = "", Selected = true });
            foreach (var pm in _paymentService.LoadAllPaymentMethods())
                model.AvailablePaymentMethods.Add(new SelectListItem { Text = pm.PluginDescriptor.FriendlyName, Value = pm.PluginDescriptor.SystemName });

            model.ShippingEnabled = _shippingSettings.Enabled;
            return View(model);
        }

        [HttpPost]
        public IActionResult OrderList(PageRequest command, OrderListModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            DateTime? startDateValue = (model.StartDate == null) ? null
                            : (DateTime?)model.StartDate.Value;

            DateTime? endDateValue = (model.EndDate == null) ? null
                            : (DateTime?)model.EndDate.Value.AddDays(1);

            OrderStatus? os = null; var ssIds = new List<int>(); var psIds = new List<int>();
            if (model.OrderStatusId > 0)
                os = (OrderStatus)model.OrderStatusId;

            if (model.PaymentStatusId > 0)
                psIds.Add(model.PaymentStatusId);

            if (model.ShippingStatusId > 0)
                ssIds.Add(model.ShippingStatusId);

            //load orders
            var orders = _orderService.SearchOrders(startTime: startDateValue, endTime: endDateValue, os: os, ssIds: ssIds,
                  pageIndex: command.Page - 1, pageSize: command.Limit);

            var ordersModel = new List<OrderModel>();
            foreach (var x in orders)
            {
                var itemModel = new OrderModel();
                itemModel.Id = x.Id;
                itemModel.OrderTotal = PriceFormatter.FormatPrice(x.OrderTotal);
                itemModel.OrderStatus = x.OrderStatus.GetLocalizedEnum(_localizationService);
                itemModel.OrderStatusId = x.OrderStatusId;
                if (x.ShippingStatus != ShippingStatus.ShippingNotRequired)
                {
                    itemModel.IsShippable = true;
                    var shipping = x.ShippingAddress;
                    itemModel.ShippingContacts = string.Format("{0} {1} {2}{3}{4} {5}", shipping?.Name, shipping?.PhoneNumber,
                        shipping?.ProvinceName, shipping?.CityName, shipping?.AreaName, shipping?.Address1);
                }
                itemModel.CreateTime = x.CreateTime;

                //subtotal
                itemModel.OrderSubtotal = PriceFormatter.FormatPrice(x.OrderSubtotal);
                itemModel.OrderSubtotalValue = x.OrderSubtotal;

                //shipping
                itemModel.OrderShipping = PriceFormatter.FormatPrice(x.OrderShipping);
                itemModel.OrderShippingValue = x.OrderShipping;
                itemModel.Remark = x.Remark;
                foreach (var op in x.orderItems)
                {
                    var opModel = new OrderModel.OrderGoodsModel()
                    {
                        Id = op.Id,
                        GoodsId = op.GoodsId,
                        Sku = op.Sku,
                        Quantity = op.Quantity
                    };
                    var defaultGoodsPicture = _pictureService.GetPicturesByGoodsId(op.GoodsId, 1).FirstOrDefault();
                    opModel.PictureThumbnailUrl = _pictureService.GetPictureUrl(defaultGoodsPicture, 75, true);
                    //goods name
                    opModel.GoodsName = op.GoodsName;
                    //unit price
                    opModel.UnitPriceValue = op.UnitPrice;
                    opModel.UnitPrice = PriceFormatter.FormatPrice(op.UnitPrice);
                    //subtotal
                    opModel.SubTotalValue = op.Price;
                    opModel.SubTotal = PriceFormatter.FormatPrice(op.Price);
                    opModel.AttributeInfo = op.AttributeDescription;
                    //return requests
                    //opModel.AfterSalesIds = _orderService.SearchAfterSales(0, op.Id, null, 0, int.MaxValue)
                    //.Select(rr => rr.Id).ToList();

                    itemModel.Items.Add(opModel);
                }
                ordersModel.Add(itemModel);

            }
            var result = new ResponseResult
            {
                data = ordersModel,
                count = orders.TotalCount
            };

            //var reportSummary = _orderReportService.GetOrderAverageReportLine
            //    (orderStatusIds, paymentStatusIds, shippingStatusIds, startDateValue, endDateValue);
            //// var profit = _orderReportService.ProfitReport(orderStatusIds, paymentStatusIds, shippingStatusIds, startDateValue, endDateValue);
            //var aggregator = new OrderModel()
            //{
            //    //  aggregatorprofit = PriceFormatter.FormatPrice(profit),
            //    aggregatortotal = PriceFormatter.FormatPrice(reportSummary.SumOrders)
            //};
            //result.ExtraData = aggregator;
            return Json(result);
        }
        #endregion

        #region Export / Import

        [HttpPost, ActionName("List")]
        [FormValueRequired("exportexcel-all")]
        public IActionResult ExportExcelAll()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            try
            {
                var orders = _orderService.GetOrdersByAffiliateId();

                byte[] bytes = null;
                using (var stream = new MemoryStream())
                {
                    _exportManager.ExportOrdersToXlsx(stream, orders);
                    bytes = stream.ToArray();
                }
                return File(bytes, "text/xls", "orders.xlsx");
            }
            catch (Exception exc)
            {
                return RedirectToAction("List");
            }
        }

        public IActionResult ExportExcelSelected(string selectedIds)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var orders = new List<Order>();
            if (selectedIds != null)
            {
                var ids = selectedIds
                    .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => Convert.ToInt32(x))
                    .ToArray();
                orders.AddRange(_orderService.GetOrdersByIds(ids));
            }

            byte[] bytes = null;
            using (var stream = new MemoryStream())
            {
                _exportManager.ExportOrdersToXlsx(stream, orders);
                bytes = stream.ToArray();
            }
            return File(bytes, "text/xls", "orders.xlsx");
        }

        #endregion

        #region Order details

        #region Payments and other order workflow

        [HttpPost]
        public IActionResult CancelOrder(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                return Json(new { error = 1 });

            try
            {
                _orderProcessingService.CancelOrder(order, true);
                return Json(new { success = 1 });
            }
            catch (Exception exc)
            {
                return Json(new { error = 1, msg = exc.Message });
            }
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("captureorder")]
        public IActionResult CaptureOrder(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            try
            {
                var errors = _orderProcessingService.Capture(order);
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                return View(model);
            }
            catch (Exception exc)
            {
                //error
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                return View(model);
            }

        }

        [HttpPost]
        public IActionResult MarkOrderAsPaid(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                return Json(new { error = 1 });
            try
            {
                _orderProcessingService.MarkOrderAsPaid(order);

                return Json(new { success = 1 });
            }
            catch (Exception exc)
            {
                return Json(new { error = 1, msg = exc.Message });
            }
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("refundorder")]
        public IActionResult RefundOrder(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            try
            {
                var errors = _orderProcessingService.Refund(order);
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                return View(model);
            }
            catch (Exception exc)
            {
                //error
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                return View(model);
            }
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("refundorderoffline")]
        public IActionResult RefundOrderOffline(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            try
            {
                _orderProcessingService.RefundOffline(order);
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                return View(model);
            }
            catch (Exception exc)
            {
                //error
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                return View(model);
            }
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("voidorder")]
        public IActionResult VoidOrder(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            try
            {
                var errors = _orderProcessingService.Void(order);
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                return View(model);
            }
            catch (Exception exc)
            {
                //error
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                return View(model);
            }
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("voidorderoffline")]
        public IActionResult VoidOrderOffline(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            try
            {
                _orderProcessingService.VoidOffline(order);
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                return View(model);
            }
            catch (Exception exc)
            {
                //error
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                return View(model);
            }
        }

        public IActionResult PartiallyRefundOrderPopup(int id, bool online)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            var model = new OrderModel();
            PrepareOrderDetailsModel(model, order);

            return View(model);
        }

        [HttpPost]
        [FormValueRequired("partialrefundorder")]
        public IActionResult PartiallyRefundOrderPopup(string btnId, string formId, int id, bool online, OrderModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            try
            {
                decimal amountToRefund = model.AmountToRefund;
                if (amountToRefund <= decimal.Zero)
                    throw new UrsException("Enter amount to refund");

                decimal maxAmountToRefund = order.OrderTotal - order.RefundedAmount;
                if (amountToRefund > maxAmountToRefund)
                    amountToRefund = maxAmountToRefund;

                var errors = new List<string>();
                if (online)
                    errors = _orderProcessingService.PartiallyRefund(order, amountToRefund).ToList();
                else
                    _orderProcessingService.PartiallyRefundOffline(order, amountToRefund);

                if (errors.Count == 0)
                {
                    //success
                    ViewBag.RefreshPage = true;
                    ViewBag.btnId = btnId;
                    ViewBag.formId = formId;

                    PrepareOrderDetailsModel(model, order);
                    return View(model);
                }
                else
                {
                    //error
                    PrepareOrderDetailsModel(model, order);
                    return View(model);
                }
            }
            catch (Exception exc)
            {
                //error
                PrepareOrderDetailsModel(model, order);
                return View(model);
            }
        }

        #endregion

        #region Edit, delete

        public IActionResult Edit(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(id);
            if (order == null || order.Deleted)
                //No order found with the specified id
                return RedirectToAction("List");

            var model = new OrderModel();
            PrepareOrderDetailsModel(model, order);

            return View(model);
        }

        [HttpPost]
        public IActionResult Delete(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            _orderProcessingService.DeleteOrder(order);
            return RedirectToAction("List");
        }

        public IActionResult PdfInvoice(int orderId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(orderId);
            var orders = new List<Order>();
            orders.Add(order);
            byte[] bytes = null;
            using (var stream = new MemoryStream())
            {
                _pdfService.PrintOrdersToPdf(stream, orders);
                bytes = stream.ToArray();
            }
            return File(bytes, "application/pdf", string.Format("order_{0}.pdf", order.Id));
        }

        public IActionResult ModifyPrice(int id)
        {
            var order = _orderService.GetOrderById(id);
            if (order == null || order.Deleted)
                return NotFound();

            var model = new OrderModel();
            PrepareOrderDetailsModel(model, order);
            return View(model);
        }

        [HttpPost]
        public IActionResult EditOrderTotal(int id, OrderModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                return Json(new { error = 1 });

            order.OrderSubtotal = model.OrderSubtotalValue;
            order.OrderShipping = model.OrderShippingValue;
            order.PaymentMethodAdditionalFee = model.PaymentMethodAdditionalFeeValue;
            order.OrderTotal = model.OrderTotalValue;
            _orderService.UpdateOrder(order);

            return Json(new { success = 1 });
        }

        [HttpPost]
        public IActionResult EditOrderGoods(int id, int opId, decimal unitPrice, int quantity)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                return Json(new { error = 1 });

            var orderGoods = order.orderItems.Where(x => x.Id == opId).FirstOrDefault();
            if (orderGoods == null)
                return Json(new { error = 1 });

            orderGoods.UnitPrice = unitPrice;
            orderGoods.Quantity = quantity;
            orderGoods.Price = unitPrice * quantity;
            _orderService.UpdateOrder(order);

            return Json(new { success = 1 });
        }

        [HttpPost]
        public IActionResult DeleteOrderGoods(int id, int opId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                return Json(new { error = 1 });

            var orderGoods = order.orderItems.Where(x => x.Id == opId).FirstOrDefault();
            if (orderGoods == null)
                return Json(new { error = 1 });

            _orderService.DeleteOrderGoods(orderGoods);

            return Json(new { success = 1 });
        }


        public IActionResult AddGoodsToOrder(int orderId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var model = new OrderModel.AddOrderGoodsModel();
            model.OrderId = orderId;
            //categories
            model.AvailableCategories.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            foreach (var c in _categoryService.GetAllCategories(showHidden: true))
                model.AvailableCategories.Add(new SelectListItem() { Text = c.GetCategoryNameWithPrefix(_categoryService), Value = c.Id.ToString() });

            //brands
            model.AvailableBrands.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            foreach (var m in _brandService.GetAll(true))
                model.AvailableBrands.Add(new SelectListItem() { Text = m.Name, Value = m.Id.ToString() });

            return View(model);
        }

        [HttpPost]
        public IActionResult AddGoodsToOrder(PageRequest command, OrderModel.AddOrderGoodsModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var result = new ResponseResult();
            var categoryIds = new List<int>();
            if (model.SearchCategoryId > 0)
                categoryIds.Add(model.SearchCategoryId);
            var list = _goodsService.SearchGoodss(out IList<int> filterableGoodsParameterOptionIds,
                categoryId: categoryIds.ToArray(), brandId: model.SearchBrandId, keywords: model.SearchGoodsName,
                pageIndex: command.Page - 1, pageSize: command.Limit);
            result.data = list.Select(x =>
            {
                var item = new OrderModel.AddOrderGoodsModel.GoodsLineModel()
                {
                    Id = x.Id,
                    Name = x.Name,
                    Sku = x.Sku,
                };
                return item;
            });
            result.count = list.TotalCount;
            return Json(result);
        }

        public IActionResult AddGoodsToOrderDetails(int orderId, int goodsId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var model = PrepareAddGoodsToOrderModel(orderId, goodsId);
            return View(model);
        }

        [HttpPost]
        public IActionResult AddGoodsToOrderDetails(int orderId, int goodsId, IFormCollection form)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(orderId);
            var goods = _goodsService.GetGoodsById(goodsId);
            //save order item

            //basic properties
            var unitPrice = decimal.Zero;
            decimal.TryParse(form["UnitPrice"], out unitPrice);
            var quantity = 1;
            int.TryParse(form["Quantity"], out quantity);
            var price = decimal.Zero;
            decimal.TryParse(form["SubTotal"], out price);

            //attributes
            //warnings
            var warnings = new List<string>();
            string attributes = "";

            #region Goods spec
            string selectedAttributes = string.Empty;
            var goodsVariantAttributes = _goodsSpecService.GetGoodsSpecMappingByGoodsId(goods.Id);
            foreach (var attribute in goodsVariantAttributes)
            {
                string controlId = string.Format("goods_attribute_{0}_{1}", attribute.GoodsSpecId, attribute.Id);
                var ctrlAttributes = form[controlId];
                if (!String.IsNullOrEmpty(ctrlAttributes))
                {
                    int selectedAttributeId = int.Parse(ctrlAttributes);
                    if (selectedAttributeId > 0)
                        selectedAttributes = _goodsSpecParser.AddGoodsSpec(selectedAttributes,
                            attribute, selectedAttributeId.ToString());
                }
            }
            attributes = selectedAttributes;

            #endregion

            //warnings
            warnings.AddRange(_shoppingCartService.GetShoppingCartItemAttributeWarnings(goods, attributes));
            if (warnings.Count == 0)
            {
                //attributes
                string attributeDescription = _goodsSpecFormatter.FormatAttributes(attributes);

                //save item
                var opv = new OrderItem()
                {
                    OrderItemGuid = Guid.NewGuid(),
                    Order = order,
                    GoodsId = goods.Id,
                    GoodsName = goods.Name,
                    UnitPrice = unitPrice,
                    Price = price,
                    AttributeDescription = attributeDescription,
                    AttributesXml = attributes,
                    Quantity = quantity
                };
                order.orderItems.Add(opv);
                _orderService.UpdateOrder(order);

                //redirect to order details page
                return RedirectToAction("Edit", "Order", new { id = order.Id });
            }
            else
            {
                //errors
                var model = PrepareAddGoodsToOrderModel(order.Id, goods.Id);
                model.Warnings.AddRange(warnings);
                return View(model);
            }
        }

        #endregion

        #endregion

        #region Addresses

        public IActionResult AddressEdit(int addressId, int orderId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            ViewBag.OrderId = orderId;

            var address = _addressService.GetAddressById(addressId);
            if (address == null)
                throw new ArgumentException("No address found with the specified id", "addressId");

            var model = new AddressModel();
            model = address.ToModel<AddressModel>();
            PreparedOrderAddress(model);

            return View(model);
        }

        [HttpPost]
        public IActionResult AddressEdit(AddressModel model, int orderId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            var address = _addressService.GetAddressById(model.Id);
            if (address == null)
                throw new ArgumentException("No address found with the specified id");

            if (ModelState.IsValid)
            {
                address = model.ToEntity(address);
                _addressService.UpdateAddress(address);

                return RedirectToAction("AddressEdit", new { addressId = model.Id, orderId = orderId });
            }

            ViewBag.OrderId = orderId;

            PreparedOrderAddress(model);
            return View(model);
        }


        [NonAction]
        public void PreparedOrderAddress(AddressModel model)
        {
            model.NameEnabled = true;
            model.NameRequired = true;
            model.CompanyEnabled = _addressSettings.CompanyEnabled;
            model.CompanyRequired = _addressSettings.CompanyRequired;
            model.ProvincesEnabled = _addressSettings.ProvincesEnabled;
            model.StreetAddressEnabled = _addressSettings.StreetAddressEnabled;
            model.StreetAddressRequired = _addressSettings.StreetAddressRequired;
            model.StreetAddress2Enabled = _addressSettings.StreetAddress2Enabled;
            model.StreetAddress2Required = _addressSettings.StreetAddress2Required;
            model.ZipPostalCodeEnabled = _addressSettings.ZipPostalCodeEnabled;
            model.ZipPostalCodeRequired = _addressSettings.ZipPostalCodeRequired;
            model.PhoneEnabled = _addressSettings.PhoneEnabled;
            model.PhoneRequired = _addressSettings.PhoneRequired;
            model.FaxEnabled = _addressSettings.FaxEnabled;
            model.FaxRequired = _addressSettings.FaxRequired;

        }
        #endregion

        #region Shipments


        public IActionResult ShipmentList()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var model = new ShipmentListModel();
            model.DisplayPdfPackagingSlip = _pdfSettings.Enabled;
            return View(model);
        }

        public IActionResult ShipmentListSelect(PageRequest command, ShipmentListModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            DateTime? startDateValue = (model.StartDate == null) ? null
                            : (DateTime?)model.StartDate.Value;

            DateTime? endDateValue = (model.EndDate == null) ? null
                            : (DateTime?)model.EndDate.Value.AddDays(1);

            var ssIds = new List<int>() { (int)ShippingStatus.Delivered, (int)ShippingStatus.PartiallyShipped, (int)ShippingStatus.Shipped, (int)ShippingStatus.NotYetShipped };

            var orders = _orderService.SearchOrders(ssIds: ssIds, pageIndex: command.Page - 1, pageSize: command.Limit);

            var sp = new List<ShipmentGoodsModel>();
            foreach (var item in orders)
                sp.AddRange(PrepareShipmentGoodsModel(item));
            var result = new ResponseResult
            {
                data = sp,
                count = orders.TotalCount
            };
            return Json(result);
        }

        [HttpPost]
        public IActionResult ShipmentsSelect(int orderId, PageRequest command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                throw new ArgumentException("No order found with the specified id");

            //shipments
            var shipmentModels = new List<ShipmentModel>();
            var shipments = order.Shipments.OrderBy(s => s.CreateTime).ToList();
            foreach (var shipment in shipments)
                shipmentModels.Add(PrepareShipmentModel(shipment, false));

            var model = new ResponseResult
            {
                data = shipmentModels,
                count = shipmentModels.Count
            };

            return Json(model);
        }

        [HttpPost]
        public IActionResult ShipmentsItemsByShipmentId(int shipmentId, PageRequest command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var shipment = _shipmentService.GetShipmentById(shipmentId);
            if (shipment == null)
                throw new ArgumentException("No shipment found with the specified id");

            var order = _orderService.GetOrderById(shipment.OrderId);
            if (order == null)
                throw new ArgumentException("No order found with the specified id");

            //shipments
            var shipmentModel = PrepareShipmentModel(shipment, true);
            var result = new ResponseResult
            {
                data = shipmentModel.Goodss,
                count = shipmentModel.Goodss.Count
            };

            return Json(result);
        }

        public IActionResult AddShipment(int orderId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            var model = new ShipmentModel()
            {
                OrderId = order.Id,
            };

            //measures
            var baseWeight = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId);
            var baseWeightIn = baseWeight != null ? baseWeight.Name : "";
            var baseDimension = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId);
            var baseDimensionIn = baseDimension != null ? baseDimension.Name : "";

            if (_shippingSettings.ActiveShippingExpressNames != null)
                foreach (var rrr in _shippingSettings.ActiveShippingExpressNames)
                {
                    model.AvailableExpressNames.Add(new SelectListItem()
                    {
                        Text = rrr,
                        Value = rrr
                    });
                }

            foreach (var opv in order.orderItems)
            {
                if (!opv.Goods.IsShipEnabled)
                    continue;

                //quantities
                var qtyInThisShipment = 0;
                var maxQtyToAdd = opv.GetTotalNumberOfItemsCanBeAddedToShipment();
                var qtyOrdered = opv.Quantity;
                var qtyInAllShipments = opv.GetTotalNumberOfItemsInAllShipment();

                //ensure that this goods can be added to a shipment
                if (maxQtyToAdd <= 0)
                    continue;

                var sopvModel = new ShipmentModel.ShipmentOrderGoodsModel()
                {
                    OrderGoodsId = opv.Id,
                    GoodsId = opv.GoodsId,
                    Sku = opv.Goods.FormatSku(opv.AttributesXml, _goodsSpecParser),
                    AttributeInfo = opv.AttributeDescription,
                    ItemWeight = opv.ItemWeight.HasValue ? string.Format("{0:F2} [{1}]", opv.ItemWeight, baseWeightIn) : "",
                    ItemDimensions = string.Format("{0:F2} x {1:F2} x {2:F2} [{3}]", opv.Goods.Length, opv.Goods.Width, opv.Goods.Height, baseDimensionIn),
                    QuantityOrdered = qtyOrdered,
                    QuantityInThisShipment = qtyInThisShipment,
                    QuantityInAllShipments = qtyInAllShipments,
                    QuantityToAdd = maxQtyToAdd,
                };

                //goods name
                sopvModel.FullGoodsName = opv.GoodsName;
                model.Goodss.Add(sopvModel);
            }

            return View(model);
        }

        [HttpPost]
        [FormValueRequired("save", "save-continue")]
        public IActionResult AddShipment(int orderId, IFormCollection form, bool continueEditing)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            Shipment shipment = null;

            decimal? totalWeight = null;
            foreach (var opv in order.orderItems)
            {
                //is shippable
                if (!opv.Goods.IsShipEnabled)
                    continue;

                //ensure that this goods can be shipped (have at least one item to ship)
                var maxQtyToAdd = opv.GetTotalNumberOfItemsCanBeAddedToShipment();
                if (maxQtyToAdd <= 0)
                    continue;

                int qtyToAdd = 0; //parse quantity
                foreach (string formKey in form.Keys)
                    if (formKey.Equals(string.Format("qtyToAdd{0}", opv.Id), StringComparison.InvariantCultureIgnoreCase))
                    {
                        int.TryParse(form[formKey], out qtyToAdd);
                        break;
                    }

                //validate quantity
                if (qtyToAdd <= 0)
                    continue;
                if (qtyToAdd > maxQtyToAdd)
                    qtyToAdd = maxQtyToAdd;

                //ok. we have at least one item. let's create a shipment (if it does not exist)

                var opvTotalWeight = opv.ItemWeight.HasValue ? opv.ItemWeight * qtyToAdd : null;
                if (opvTotalWeight.HasValue)
                {
                    if (!totalWeight.HasValue)
                        totalWeight = 0;
                    totalWeight += opvTotalWeight.Value;
                }
                if (shipment == null)
                {
                    shipment = new Shipment()
                    {
                        OrderId = order.Id,
                        ShippingMethod = order.ShippingMethod,
                        ExpressName = form["ExpressName"],
                        TrackingNumber = form["TrackingNumber"],
                        TotalWeight = null,
                        ShippedTime = null,
                        DeliveryTime = null,
                        CreateTime = DateTime.Now,
                    };
                }
                //create a shipment order goods 
                var sopv = new ShipmentOrderItem()
                {
                    OrderItemId = opv.Id,
                    Quantity = qtyToAdd,
                };
                shipment.ShipmentorderItems.Add(sopv);
            }

            //if we have at least one item in the shipment, then save it
            if (shipment != null && shipment.ShipmentorderItems.Count > 0)
            {
                shipment.TotalWeight = totalWeight;
                _shipmentService.InsertShipment(shipment);
                _orderProcessingService.Ship(shipment, false);

                return continueEditing
                           ? RedirectToAction("ShipmentDetails", new { id = shipment.Id })
                           : RedirectToAction("Edit", new { id = orderId });
            }
            else
            {
                return RedirectToAction("AddShipment", new { orderId = orderId });
            }
        }

        public IActionResult ShipmentDetails(int orderId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                return Content("");

            var shipment = _shipmentService.GetShipmentByOrderId(orderId);

            var model = shipment == null ? new ShipmentModel() { OrderId = orderId } : PrepareShipmentModel(shipment, true);
            model.ShippingAddress = order.ShippingAddress.ToModel<AddressModel>();
            if (_shippingSettings.ActiveShippingExpressNames != null)
                foreach (var rrr in _shippingSettings.ActiveShippingExpressNames)
                {
                    model.AvailableExpressNames.Add(new SelectListItem()
                    {
                        Text = rrr,
                        Value = rrr
                    });
                }
            return View(model);
        }

        [HttpPost]
        public IActionResult ShipmentDetails(ShipmentModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var shipment = _shipmentService.GetShipmentById(model.Id);
            if (shipment == null)
            {
                var order = _orderService.GetOrderById(model.OrderId);
                shipment = new Shipment()
                {
                    OrderId = order.Id,
                    ShippingMethod = order.ShippingMethod,
                    ExpressName = model.ExpressName,
                    TrackingNumber = model.TrackingNumber,
                    CreateTime = DateTime.Now,
                };
                decimal? totalWeight = null;
                foreach (var opv in order.orderItems)
                {
                    if (!opv.Goods.IsShipEnabled)
                        continue;
                    var opvTotalWeight = opv.ItemWeight.HasValue ? opv.ItemWeight * opv.Quantity : null;
                    if (opvTotalWeight.HasValue)
                    {
                        if (!totalWeight.HasValue)
                            totalWeight = 0;
                        totalWeight += opvTotalWeight.Value;
                    }
                    //create a shipment order goods 
                    var sopv = new ShipmentOrderItem()
                    {
                        OrderItemId = opv.Id,
                        Quantity = opv.Quantity,
                    };
                    shipment.ShipmentorderItems.Add(sopv);
                }
                shipment.TotalWeight = totalWeight;
                _shipmentService.InsertShipment(shipment);
                _orderProcessingService.Ship(shipment, false);
            }
            else
            {
                shipment.TrackingNumber = model.TrackingNumber;
                shipment.ExpressName = model.ExpressName;
                _shipmentService.UpdateShipment(shipment);

                _orderProcessingService.Ship(shipment, false);
            }
            return Json(new { success = 1 });
        }

        [HttpPost]
        public IActionResult SetAsShipped(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var shipment = _shipmentService.GetShipmentById(id);
            if (shipment == null)
                return Json(new { error = 1 });

            try
            {
                _orderProcessingService.Ship(shipment, false);
                return Json(new { success = 1 });
            }
            catch (Exception exc)
            {
                return Json(new { error = 1, msg = exc.Message });
            }
        }

        [HttpPost]
        public IActionResult SetAsDelivered(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var shipment = _shipmentService.GetShipmentById(id);
            if (shipment == null)
                return Json(new { error = 1 });

            try
            {
                _orderProcessingService.Deliver(shipment, false);
                return Json(new { success = 1 });
            }
            catch (Exception exc)
            {
                return Json(new { error = 1, msg = exc.Message });
            }
        }

        [HttpPost]
        public IActionResult DeleteShipment(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var shipment = _shipmentService.GetShipmentById(id);
            if (shipment == null)
                //No shipment found with the specified id
                return RedirectToAction("List");

            var orderId = shipment.OrderId;

            _shipmentService.DeleteShipment(shipment);
            return RedirectToAction("Edit", new { id = orderId });
        }

        public IActionResult PdfPackagingSlip(int shipmentId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var shipment = _shipmentService.GetShipmentById(shipmentId);
            if (shipment == null)
                //no shipment found with the specified id
                return RedirectToAction("List");

            var order = shipment.Order;

            var shipments = new List<Shipment>();
            shipments.Add(shipment);

            byte[] bytes = null;
            using (var stream = new MemoryStream())
            {
                _pdfService.PrintPackagingSlipsToPdf(stream, shipments);
                bytes = stream.ToArray();
            }
            return File(bytes, "application/pdf", string.Format("packagingslip_{0}.pdf", shipment.Id));
        }

        public IActionResult PdfPackagingSlipAll()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var shipments = _shipmentService.GetAllShipments(null, null);

            byte[] bytes = null;
            using (var stream = new MemoryStream())
            {
                _pdfService.PrintPackagingSlipsToPdf(stream, shipments);
                bytes = stream.ToArray();
            }
            return File(bytes, "application/pdf", "packagingslips.pdf");
        }

        public IActionResult PdfPackagingSlipSelected(string selectedIds)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var shipments = new List<Shipment>();
            if (selectedIds != null)
            {
                var ids = selectedIds
                    .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => Convert.ToInt32(x))
                    .ToArray();
                shipments.AddRange(_shipmentService.GetShipmentsByIds(ids));
            }

            byte[] bytes = null;
            using (var stream = new MemoryStream())
            {
                _pdfService.PrintPackagingSlipsToPdf(stream, shipments);
                bytes = stream.ToArray();
            }
            return File(bytes, "application/pdf", "packagingslips.pdf");
        }


        [HttpPost]
        public IActionResult SetAsShippedSelected(ICollection<int> selectedIds)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var shipments = new List<Shipment>();
            if (selectedIds != null)
            {
                shipments.AddRange(_shipmentService.GetShipmentsByIds(selectedIds.ToArray()));
            }

            foreach (var shipment in shipments)
            {
                try
                {
                    _orderProcessingService.Ship(shipment, true);
                }
                catch
                {
                    //ignore any exception
                }
            }

            return Json(new { Result = true });
        }

        [HttpPost]
        public IActionResult SetAsDeliveredSelected(ICollection<int> selectedIds)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var shipments = new List<Shipment>();
            if (selectedIds != null)
            {
                shipments.AddRange(_shipmentService.GetShipmentsByIds(selectedIds.ToArray()));
            }

            foreach (var shipment in shipments)
            {
                try
                {
                    _orderProcessingService.Deliver(shipment, true);
                }
                catch
                {
                    //ignore any exception
                }
            }

            return Json(new { Result = true });
        }

        #endregion

        #region Order notes

        [HttpPost]
        public IActionResult OrderNotesSelect(int orderId, PageRequest command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                return Json(new { success = 1 });

            //order notes
            var orderNoteModels = new List<OrderModel.OrderNote>();
            foreach (var orderNote in order.OrderNotes
                .OrderByDescending(on => on.CreateTime))
            {
                orderNoteModels.Add(new OrderModel.OrderNote()
                {
                    Id = orderNote.Id,
                    OrderId = orderNote.OrderId,
                    DisplayToUser = orderNote.DisplayToUser,
                    Note = orderNote.FormatOrderNoteText(),
                    CreateTime = orderNote.CreateTime
                });
            }

            var model = new ResponseResult
            {
                data = orderNoteModels,
                count = orderNoteModels.Count
            };

            return Json(model);
        }

        [HttpPost]
        public IActionResult OrderNoteAdd(int orderId, string message)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                return Json(new { error = 1 });

            var orderNote = new OrderNote()
            {
                DisplayToUser = false,
                Note = message,
                CreateTime = DateTime.Now,
            };
            order.OrderNotes.Add(orderNote);
            _orderService.UpdateOrder(order);

            return Json(new { success = 1 });
        }

        [HttpPost]
        public IActionResult OrderNoteDelete(int id, int noteId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return HttpUnauthorized();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                return Json(new { error = 1 });

            var orderNote = order.OrderNotes.FirstOrDefault(on => on.Id == noteId);
            if (orderNote == null)
                return Json(new { error = 1 });

            _orderService.DeleteOrderNote(orderNote);

            return Json(new { success = 1 });
        }


        #endregion

        #region Reports

        [NonAction]
        protected IList<BestsellersReportLineModel> GetBestsellersBriefReportModel(int recordsToReturn, int orderBy)
        {
            var report = _orderReportService.BestSellersReport(recordsToReturn: recordsToReturn, orderBy: orderBy, groupBy: 1, showHidden: true);

            var model = report.Select(x =>
            {
                var m = new BestsellersReportLineModel()
                {
                    GoodsId = x.EntityId,
                    TotalAmount = PriceFormatter.FormatPrice(x.TotalAmount),
                    TotalQuantity = x.TotalQuantity,
                };
                var goods = _goodsService.GetGoodsById(x.EntityId);
                if (goods != null)
                    m.GoodsName = goods.Name;
                return m;
            }).ToList();

            return model;
        }
        public IActionResult BestsellersBriefReportByQuantity()
        {
            var model = GetBestsellersBriefReportModel(5, 1);
            return PartialView(model);
        }

        public IActionResult BestsellersBriefReportByQuantityList(PageRequest command)
        {
            var model = GetBestsellersBriefReportModel(5, 1);
            var result = new ResponseResult
            {
                data = model,
                count = model.Count
            };
            return Json(result);
        }
        public IActionResult BestsellersBriefReportByAmount()
        {
            var model = GetBestsellersBriefReportModel(5, 2);
            return PartialView(model);
        }

        public IActionResult BestsellersBriefReportByAmountList(PageRequest command)
        {
            var model = GetBestsellersBriefReportModel(5, 2);
            var result = new ResponseResult
            {
                data = model,
                count = model.Count
            };
            return Json(result);
        }

        [NonAction]
        protected virtual IList<OrderAverageReportLineSummaryModel> GetOrderAverageReportModel()
        {
            var report = new List<OrderAverageReportLineSummary>();
            report.Add(_orderReportService.OrderAverageReport(OrderStatus.Pending));
            report.Add(_orderReportService.OrderAverageReport(OrderStatus.Processing));
            report.Add(_orderReportService.OrderAverageReport(OrderStatus.Reviewing));
            report.Add(_orderReportService.OrderAverageReport(OrderStatus.Complete));
            report.Add(_orderReportService.OrderAverageReport(OrderStatus.Cancelled));
            var model = report.Select(x =>
            {
                return new OrderAverageReportLineSummaryModel()
                {
                    OrderStatus = x.OrderStatus.GetLocalizedEnum(_localizationService),
                    SumTodayOrders = PriceFormatter.FormatPrice(x.SumTodayOrders),
                    SumThisWeekOrders = PriceFormatter.FormatPrice(x.SumThisWeekOrders),
                    SumThisMonthOrders = PriceFormatter.FormatPrice(x.SumThisMonthOrders),
                    SumThisYearOrders = PriceFormatter.FormatPrice(x.SumThisYearOrders),
                    SumAllTimeOrders = PriceFormatter.FormatPrice(x.SumAllTimeOrders),
                };
            }).ToList();

            return model;
        }

        public IActionResult OrderAverageReport()
        {
            var model = GetOrderAverageReportModel();
            return PartialView(model);
        }

        public IActionResult OrderAverageReportList(PageRequest command)
        {
            var model = GetOrderAverageReportModel();
            var result = new ResponseResult
            {
                data = model,
                count = model.Count
            };
            return Json(result);
        }

        [NonAction]
        protected virtual IList<OrderIncompleteReportLineModel> GetOrderIncompleteReportModel()
        {
            var model = new List<OrderIncompleteReportLineModel>();
            //not paid
            var orderStatuses = Enum.GetValues(typeof(OrderStatus)).Cast<int>().Where(os => os != (int)OrderStatus.Cancelled).ToList();
            var paymentStatuses = new List<int>() { (int)PaymentStatus.Pending };
            var psPending = _orderReportService.GetOrderAverageReportLine(psIds: paymentStatuses, osIds: orderStatuses);
            model.Add(new OrderIncompleteReportLineModel()
            {
                Item = _localizationService.GetResource("Admin.SalesReport.Incomplete.TotalUnpaidOrders"),
                Count = psPending.CountOrders,
                Total = PriceFormatter.FormatPrice(psPending.SumOrders),
                ViewLink = Url.Action("List", "Order", new
                {
                    orderStatusIds = string.Join(",", orderStatuses),
                    paymentStatusIds = string.Join(",", paymentStatuses)
                })
            });
            //not shipped
            var shippingStatuses = new List<int>() { (int)ShippingStatus.NotYetShipped };
            var ssPending = _orderReportService.GetOrderAverageReportLine(psIds: shippingStatuses, osIds: orderStatuses);
            model.Add(new OrderIncompleteReportLineModel()
            {
                Item = _localizationService.GetResource("Admin.SalesReport.Incomplete.TotalNotShippedOrders"),
                Count = ssPending.CountOrders,
                Total = PriceFormatter.FormatPrice(ssPending.SumOrders),
                ViewLink = Url.Action("List", "Order", new
                {
                    orderStatusIds = string.Join(",", orderStatuses),
                    shippingStatusIds = string.Join(",", shippingStatuses)
                })
            });
            //pending
            orderStatuses = new List<int>() { (int)OrderStatus.Pending };
            var osPending = _orderReportService.GetOrderAverageReportLine(osIds: orderStatuses);
            model.Add(new OrderIncompleteReportLineModel()
            {
                Item = _localizationService.GetResource("Admin.SalesReport.Incomplete.TotalIncompleteOrders"),
                Count = osPending.CountOrders,
                Total = PriceFormatter.FormatPrice(osPending.SumOrders),
                ViewLink = Url.Action("List", "Order", new { orderStatusIds = string.Join(",", orderStatuses) })
            });
            return model;
        }

        public IActionResult OrderIncompleteReport()
        {
            var model = GetOrderIncompleteReportModel();
            return PartialView(model);
        }

        public IActionResult OrderIncompleteReportList(PageRequest command)
        {
            var model = GetOrderIncompleteReportModel();
            var result = new ResponseResult
            {
                data = model,
                count = model.Count
            };
            return Json(result);
        }

        #endregion
    }
}
